#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define mes_len 256 //固定长度
int client_fd = 0;


void *send_mes(void* agv)//发送消息函数
{
    while(1)
    {
        char buff[mes_len]="";
        scanf("%s",buff);
        if (strcmp(buff,"close")==0)break;//判断输入的是否是关闭指令
        send(client_fd,buff,mes_len,0);
    }
   
    return 0;
}


void *recv_mes(void* agv)//接受消息函数
{
    struct sockaddr_in *addr_in = (struct sockaddr_in *)agv;
    while(1)
    {
        char buf[mes_len] = "";//存储接受到的消息
        int recv_len = recv(client_fd,buf,mes_len,0);
        char ip_str[INET_ADDRSTRLEN] = "";//存储后面转换成字符串的ip
        inet_ntop(AF_INET,&addr_in->sin_addr.s_addr,ip_str,sizeof(ip_str));//将s_addr里面的内容按每个字节分开组成字符串放到ip_str里面
        printf("%s(%d):%s(%d)\n",ip_str,ntohs(addr_in->sin_port),buf,recv_len);//打印接受到的消息
    }
    return 0;
}


int argv_ex(int argc_in,char const *argv_in)
{
    //此函数用来判断输入参数的个数及类型,所以需要一个参数,如果缺少参数或者多输入参数,则会打印提示并退出
    if ((argc_in != 2))
    {
        printf("输入格式不对!格式: a.out 端口\n");
        return -1;
    }
    for (int i = 0; i < strlen(argv_in); i++)
    {   //通过循环和下标,对输入的参数进行逐个检查,如果不为数字,就打印提示并返回
        if (argv_in[1]<'0'||argv_in[1]>'9')
        {
            printf("参数必须是数值!\n");
            return -1;
        }
    }
    //判断输入的参数是否在范围内,否则打印提示并退出
    if ((atoi(argv_in)>65535) || (atoi(argv_in)<1))
    {
        printf("输入参数不对!参数为端口,数值范围为1-65535!\n");
        return -1;
    }
    return 0;
}

int main(int argc, char const *argv[])
{
    int ex_argv_num = argv_ex(argc,argv[1]);//检查输入参数
    if (ex_argv_num < 0)return 0;
    int sockfd = 0;
    if ((sockfd=socket(AF_INET,SOCK_STREAM,0)) < 0)//创建套接字描述符,并判断是否创建成功,如果未创建成功就直接结束并打印错误信息
    {
        perror("sockfd");
        return 0;
    }
    struct sockaddr_in bind_addr;//创建需要绑定的本地ip结构体
    bzero(&bind_addr,sizeof(bind_addr));//初始化本地ip结构体
    bind_addr.sin_family = AF_INET;//协议
    bind_addr.sin_port = htons(atoi(argv[1]));//通过输入的参数绑定本地端口
    bind_addr.sin_addr.s_addr = INADDR_ANY;//INADDR_ANY 绑定本地机的所有IP地址 
    // 为套接字描述符绑定本地ip和端口并通过bind的返回值判断是否绑定成功,未成功就关闭描述符并且打印错误信息以及直接结束
    if (bind(sockfd, (struct sockaddr *)&bind_addr, sizeof(bind_addr)) < 0)
    {
        perror("bind");
        close(sockfd);
        return 0;
    }

    listen(sockfd,10);
    while (1)
    {
        struct sockaddr_in client_addr;
        memset(&client_addr,0,sizeof(client_addr));
        socklen_t len = sizeof(client_addr);
        client_fd = accept(sockfd,(struct sockaddr *)&client_addr,&len);
        pthread_t thread1;//创建第一个线程号
        pthread_t thread2;//创建第二个线程号
        pthread_create(&thread2,NULL,recv_mes,&client_addr);
        pthread_create(&thread1,NULL,send_mes,NULL);//绑定线程号和函数,并且调用线程,传入参数描述符
    }
    close(sockfd);//最后关闭描述符
    return 0;
}
